home *** CD-ROM | disk | FTP | other *** search
- /* ----------------------------------------------------------------
- * FILE
- * pqcomm.c
- *
- * DESCRIPTION
- * Communication functions between the Frontend and the Backend
- *
- * INTERFACE ROUTINES
- * pq_gettty - return the name of the tty in the given buffer
- * pq_getport - return the PGPORT setting or 4321 if not set
- * pq_close - close input / output connections
- * pq_flush - flush pending output
- * pq_getstr - get a null terminated string from connection
- * pq_getnchar - get n characters from connection
- * pq_getint - get an integer from connection
- * pq_putstr - send a null terminated string to connection
- * pq_putnchar - send n characters to connection
- * pq_putint - send an integer to connection
- * pq_getinaddr - initialize address from host and port number
- * pq_getinserv - initialize address from host and service name
- * pq_connect - create remote input / output connection
- * pq_accept - accept remote input / output connection
- * pq_async_notify - receive notification from backend.
- *
- * NOTES
- * These functions are used by both frontend applications and
- * the postgres backend.
- *
- * IDENTIFICATION
- * $Header: /private/postgres/src/lib/libpq/RCS/pqcomm.c,v 1.18 1992/04/21 13:13:53 clarsen Exp $
- * ----------------------------------------------------------------
- */
-
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <fcntl.h>
- #include <netdb.h>
- #include <netinet/in.h>
- #include <signal.h>
-
- #include "tmp/pqcomm.h"
- #include "tmp/c.h"
- #include "utils/log.h"
-
- RcsId ("$Header: /private/postgres/src/lib/libpq/RCS/pqcomm.c,v 1.18 1992/04/21 13:13:53 clarsen Exp $");
-
- /* ----------------
- * declarations
- * ----------------
- */
- extern char **environ;
- char **ep;
- char *dp;
-
- FILE *Pfout, *Pfin;
- int PQAsyncNotifyWaiting; /* for async. notification */
- extern char PQerrormsg[];
-
- char *strcpy(), *ttyname();
-
- /* forward declarations */
- void pq_regoob ARGS((void (*fptr )()));
- void pq_unregoob ARGS((void ));
- void pq_async_notify ARGS((void ));
-
-
- /* --------------------------------
- * pq_init - open portal file descriptors
- * --------------------------------
- */
- void
- pq_init(fd)
- int fd;
- {
- Pfin = fdopen(fd, "r");
- Pfout = fdopen(dup(fd), "w");
- if (!Pfin || !Pfout)
- elog(FATAL, "Couldn't initialize socket connection");
- PQnotifies_init();
- }
-
- /* --------------------------------
- * pq_gettty - return the name of the tty in the given buffer
- * --------------------------------
- */
- void
- pq_gettty(tp)
- char tp[20];
- {
- strcpy(tp, ttyname(0));
- }
-
- /* --------------------------------
- * pq_getport - return the PGPORT setting or 4321 if not set
- * --------------------------------
- */
- int
- pq_getport()
- {
- char *env, *getenv();
- int port_no;
-
- env = getenv("PGPORT");
-
- if (env) {
- port_no = atoi(env);
- } else
- port_no = 4321;
-
- return
- port_no;
- }
-
- /* --------------------------------
- * pq_close - close input / output connections
- * --------------------------------
- */
- void
- pq_close()
- {
- if (Pfin) {
- fclose(Pfin);
- Pfin = NULL;
- }
- if (Pfout) {
- fclose(Pfout);
- Pfout = NULL;
- }
- PQAsyncNotifyWaiting = 0;
- PQnotifies_init();
- pq_unregoob();
- }
-
- /* --------------------------------
- * pq_flush - flush pending output
- * --------------------------------
- */
- void
- pq_flush()
- {
- if (Pfout)
- fflush(Pfout);
- }
-
- /* --------------------------------
- * pq_getstr - get a null terminateed string from connection
- * --------------------------------
- */
- int
- pq_getstr(s, maxlen)
- char *s;
- int maxlen;
- {
- int c;
-
- if (Pfin == (FILE *) NULL)
- {
- elog(DEBUG, "Input descriptor is null");
- return (EOF);
- }
-
- while (maxlen-- && (c = getc(Pfin)) != EOF && c)
- *s++ = c;
- *s = '\0';
-
- /* -----------------
- * If EOF reached let caller know
- * -----------------
- */
- if (c == EOF)
- return EOF;
- else
- return !EOF;
- }
-
- /*
- * USER FUNCTION - gets a newline-terminated string from the backend.
- *
- * Chiefly here so that applications can use "COPY <rel> () from stdin"
- * and read the output string. Returns a null-terminated string in s and
- * EOF if it is detected.
- */
-
- int
- PQgetline(s, maxlen)
-
- char *s;
- int maxlen;
-
- {
- int c;
-
- if (Pfin == (FILE *) NULL)
- return (EOF);
-
- while (maxlen-- && (c = getc(Pfin)) != '\n' && c != EOF)
- {
- *s++ = c;
- }
- *s = '\0';
-
- /* -----------------
- * If EOF reached let caller know
- * -----------------
- */
- if (c == EOF)
- return EOF;
- else
- return !EOF;
- }
-
- /*
- * USER FUNCTION - sends a string to the backend.
- *
- * Chiefly here so that applications can use "COPY <rel> () to stdout"
- * and read the output string. Returns a null-terminated string in s and
- * EOF if it is detected.
- */
-
- int
- PQputline(s)
-
- char *s;
-
- {
- if (Pfout) {
- (void) fputs(s, Pfout);
- fflush(Pfout);
- }
- }
-
- /* --------------------------------
- * pq_getnchar - get n characters from connection
- * --------------------------------
- */
- int
- pq_getnchar(s, off, maxlen)
- char *s;
- int off, maxlen;
- {
- int c;
-
- if (Pfin == (FILE *) NULL)
- {
- elog(DEBUG, "Input descriptor is null");
- return (EOF);
- }
-
- s += off;
- while (maxlen-- && (c = getc(Pfin)) != EOF)
- *s++ = c;
-
- /* -----------------
- * If EOF reached let caller know
- * -----------------
- */
- if (c == EOF)
- {
- return EOF;
- }
- else
- return !EOF;
- }
-
- /* --------------------------------
- * pq_getint - get an integer from connection
- * --------------------------------
- */
- int
- pq_getint(b)
- int b;
- {
- int n, c, p;
-
- if (Pfin == (FILE *) NULL)
- {
- elog(DEBUG, "Input descriptor is null");
- return (EOF);
- }
-
- n = p = 0;
- while (b-- && (c = getc(Pfin)) != EOF && p < 32) {
- n |= (c & 0xff) << p;
- p += 8;
- }
-
- return n;
- }
-
- /* --------------------------------
- * pq_putstr - send a null terminated string to connection
- * --------------------------------
- */
- void
- pq_putstr(s)
- char *s;
- {
- int status;
-
- if (Pfout) {
- status = fputs(s, Pfout);
- if (status == EOF)
- {
- strcpy(PQerrormsg,"pq_putstr: write to backend failed\n");
- fprintf(stderr, PQerrormsg);
- }
- status = fputc('\0', Pfout);
- if (status == EOF)
- {
- strcpy(PQerrormsg, "pq_putstr: write to backend failed\n");
- fprintf(stderr, PQerrormsg);
- }
- }
- }
-
- /* --------------------------------
- * pq_putnchar - send n characters to connection
- * --------------------------------
- */
- void
- pq_putnchar(s, n)
- char *s;
- int n;
- {
- int status;
-
- if (Pfout) {
- while (n--)
- {
- status = fputc(*s++, Pfout);
- if (status == EOF)
- {
- strcpy(PQerrormsg, "pq_putnchar: write to backend failed\n");
- fprintf(stderr, PQerrormsg);
- }
- }
- }
- }
-
- /* --------------------------------
- * pq_putint - send an integer to connection
- * --------------------------------
- */
- void
- pq_putint(i, b)
- int i, b;
- {
- int status;
-
- if (b > 4)
- b = 4;
-
- if (Pfout) {
- while (b--) {
- status = fputc(i & 0xff, Pfout);
- i >>= 8;
- if (status == EOF)
- {
- strcpy(PQerrormsg, "pq_putint: write to backend failed\n");
- fprintf(stderr, PQerrormsg);
- }
- }
- }
- }
-
- /* ---
- * pq_sendoob - send a string over the out-of-band channel
- * pq_recvoob - receive a string over the oob channel
- * NB: Fortunately, the out-of-band channel doesn't conflict with
- * buffered I/O because it is separate from regular com. channel.
- * ---
- */
- int
- pq_sendoob(msg,len)
- char *msg;
- int len;
- {
- int fd = fileno(Pfout);
- return send(fd,msg,len,MSG_OOB);
- }
-
- int
- pq_recvoob(msgPtr,lenPtr)
- char *msgPtr;
- int *lenPtr;
- {
- int fd = fileno(Pfout);
- int len = 0, n;
- len = recv(fd,msgPtr+len,*lenPtr,MSG_OOB);
- *lenPtr = len;
- return len;
- }
-
- /* --------------------------------
- * pq_getinaddr - initialize address from host and port number
- * --------------------------------
- */
- int
- pq_getinaddr(sin, host, port)
- struct sockaddr_in *sin;
- char *host;
- int port;
- {
- struct hostent *hs;
-
- bzero((char *)sin, sizeof *sin);
-
- if (host) {
- if (*host >= '0' && *host <= '9')
- sin->sin_addr.s_addr = inet_addr(host);
- else {
- if (!(hs = gethostbyname(host))) {
- perror(host);
- return(1);
- }
- if (hs->h_addrtype != AF_INET) {
- sprintf(PQerrormsg,"%s: Not Internet\n",host);
- fprintf(stderr,PQerrormsg);
- return(1);
- }
- bcopy(hs->h_addr, (char *)&sin->sin_addr, hs->h_length);
- }
- }
-
- sin->sin_family = AF_INET;
- sin->sin_port = htons(port);
-
- return(0);
- }
-
- /* --------------------------------
- * pq_getinserv - initialize address from host and servive name
- * --------------------------------
- */
- int
- pq_getinserv(sin, host, serv)
- struct sockaddr_in *sin;
- char *host, *serv;
- {
- struct servent *ss;
-
- if (*serv >= '0' && *serv <= '9')
- return
- pq_getinaddr(sin, host, atoi(serv));
-
- if (!(ss = getservbyname(serv, NULL))) {
- fputs(serv, stderr);
- fputs(": Unknown service\n", stderr);
- sprintf(PQerrormsg,"%s: Unknown service\n",serv);
- return(1);
- }
-
- return
- pq_getinaddr(sin, host, ntohs(ss->s_port));
- }
-
- /* ----------------------------------------
- * pq_connect -- initiate a communication link between client and
- * POSTGRES backend via postmaster.
- *
- * RETURNS: STATUS_ERROR, if arguments are wrong or local communication
- * status is screwed up (can't create socket, etc). STATUS_OK
- * otherwise.
- *
- * SIDE_EFFECTS: initiates connection.
- * SIGURG handler is set (async notification)
- *
- * NOTE: we don't wait for any error messages from the backend/postmaster.
- * That means that if the fork fails or the startup message is corrupted,
- * we won't find out until the first Send/Receive message cal.
- * ----------------------------------------
- */
- int
- pq_connect(dbname,user,args,hostName,debugTty,execFile,portName)
- char *dbname;
- char *user;
- char *args;
- char *hostName;
- char *debugTty;
- char *execFile;
- short portName;
- {
- /*
- * This data structure is used for the seq-packet protocol. It
- * describes the frontend-backend connection.
- */
- Connection *MyConn = NULL;
- Port *SendPort = NULL; /* This is a TCP or UDP socket */
- StartupPacket startup;
- int sock;
- Addr addr;
- int status;
-
- /*
- * Initialize the startup packet. Packet fields defined in comm.h
- */
- strncpy(startup.database,dbname,sizeof(startup.database));
- strncpy(startup.user,user,sizeof(startup.user));
- strncpy(startup.options,args,sizeof(startup.options));
- strncpy(startup.tty,debugTty,sizeof(startup.tty));
- if (execFile != NULL)
- {
- strncpy(startup.execFile, execFile, sizeof(startup.execFile));
- }
- else
- {
- strncpy(startup.execFile, "", sizeof(startup.execFile));
- }
-
- /* If no port was suggested grab the default or PGPORT value */
- if (!portName)
- portName = pq_getport();
- /*
- * initialize connection structure. This is really needed
- * only for the sequenced packet protocol, but these initializations
- * are important to the packet.c library.
- */
- MyConn = (Connection *) malloc(sizeof(Connection));
- bzero(MyConn, sizeof(Connection));
- MyConn->id = INVALID_ID;
- MyConn->seqno = INITIAL_SEQNO;
-
- /*
- * Open a connection to postmaster/backend.
- */
- status = StreamOpen(hostName,portName,&sock);
- if (status != STATUS_OK)
- return(STATUS_ERROR);
-
- /*
- * Save communication port state.
- */
- SendPort = (Port *) malloc(sizeof(Port));
- bzero(SendPort, sizeof(Port));
- SendPort->sock = sock;
- /*
- * kai: Linux TCP/IP wants sock.addr filled by the receiver
- */
- #ifdef linux
- {
- struct hostent *hp;
-
- /* The gethostbyname() just succeeded for the StreamOpen() call, so
- it shouldn't fail, but who knows. */
- if (! hostName)
- hostName = "localhost";
- if ((hp = gethostbyname(hostName)) && hp->h_addrtype == AF_INET)
- bcopy(hp->h_addr, (char *)&(SendPort -> addr.sin_addr), hp->h_length);
- else {
- fprintf(stderr,
- "StreamServerPort: cannot find hostname '%s' for stream port\n",
- hostName);
- return(STATUS_ERROR);
- }
- SendPort -> addr.sin_family = AF_INET;
- SendPort -> addr.sin_port = htons(portName);
- }
- #endif
-
- /* initialize */
- status = PacketSend(SendPort, MyConn,
- &startup, STARTUP_MSG, sizeof(startup), BLOCKING);
-
- /* set up streams over which communic. will flow */
- Pfout = fdopen(sock, "w");
- Pfin = fdopen(dup(sock), "r");
- if (!Pfout && !Pfin)
- {
- strcpy(PQerrormsg, "Couldn't fdopen the socket descriptor\n");
- fprintf(stderr,PQerrormsg);
- return(STATUS_ERROR);
- }
-
- PQAsyncNotifyWaiting = 0;
- PQnotifies_init();
- pq_regoob(pq_async_notify);
-
- if (status != STATUS_OK)
- return(STATUS_ERROR);
-
- return(STATUS_OK);
- }
-
- /* --------------------------------
- * pq_accept - accept remote input / output connection
- * --------------------------------
- */
- int
- pq_accept()
- {
- struct sockaddr_in sin;
- int fd, nfd, i;
-
- if (!(fd = socket(AF_INET, SOCK_STREAM, 0)))
- return(-2);
-
- pq_getinaddr(&sin, 0, pq_getport());
- if (bind(fd, &sin, sizeof sin))
- return(-3);
-
- /* kai: the man-page of listen() tells, that there is a maximum of 5
- unprocessed connects, so I assume SOMAXCONN = 5 */
- #if defined(linux) && ! defined(SOMAXCONN)
- #define SOMAXCONN 5
- #endif
- listen(fd, SOMAXCONN);
- if ((nfd = accept(fd, NULL, NULL)) < 0)
- return(-1);
-
- close(fd);
- Pfout = fdopen(nfd, "w");
- Pfin = fdopen(dup(nfd), "r");
-
- return(0);
- }
-
- /*
- * register an out-of-band listener proc--at most one allowed.
- * This is used for receiving async. notification from the backend.
- */
- void
- pq_regoob(fptr)
- void (*fptr)();
- {
- int fd = fileno(Pfout);
- fcntl(fd,F_SETOWN,getpid());
- (void) signal(SIGURG,fptr);
- }
-
- void pq_unregoob()
- {
- signal(SIGURG,SIG_DFL);
- }
-
-
- void pq_async_notify() {
- char msg[20];
- int len = sizeof(msg);
- if (pq_recvoob(msg,&len) >= 0) {
- /* debugging */
- printf("received notification: %s\n",msg);
- PQAsyncNotifyWaiting = 1;
- /* PQappendNotify(msg+1);*/
- } else {
- extern int errno;
- printf("SIGURG but no data: len = %d, err=%d\n",len,errno);
- }
- }
-
- /*
- * Streams -- wrapper around Unix socket system calls
- *
- *
- * Stream functions are used for vanilla TCP connection protocol.
- */
-
- /*
- * StreamServerPort -- open a sock stream "listening" port.
- *
- * This initializes the Postmaster's connection
- * accepting port.
- *
- * ASSUME: that this doesn't need to be non-blocking because
- * the Postmaster uses select() to tell when the socket
- * is ready.
- *
- * RETURNS: STATUS_OK or STATUS_ERROR
- */
- StreamServerPort(hostName,portName,fdP)
- char *hostName;
- short portName;
- int *fdP;
- {
- struct sockaddr_in sin;
- int fd;
- struct hostent *hp;
-
- if (! hostName)
- hostName = "localhost";
-
- bzero((char *)&sin, sizeof sin);
-
- #ifdef NOTDEF
- if ((hp = gethostbyname(hostName)) && hp->h_addrtype == AF_INET)
- bcopy(hp->h_addr, (char *)&(sin.sin_addr), hp->h_length);
- else {
- fprintf(stderr,
- "StreamServerPort: cannot find hostname '%s' for stream port\n",
- hostName);
- return(STATUS_ERROR);
- }
- #endif
-
- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
- strcpy(PQerrormsg,"StreamServerPort: cannot make socket descriptor for port\n");
- fprintf(stderr,PQerrormsg);
- return(STATUS_ERROR);
- }
-
- sin.sin_family = AF_INET;
- sin.sin_port = htons(portName);
-
- if (bind(fd, (char *)&sin, sizeof sin) < 0) {
- strcpy(PQerrormsg,"StreamServerPort: cannot bind to port\n");
- fprintf(stderr,PQerrormsg);
- return(STATUS_ERROR);
- }
-
- listen(fd, SOMAXCONN);
-
- /* MS: I took this code from Dillon's version. It makes the
- * listening port non-blocking. That is not necessary (and
- * may tickle kernel bugs).
-
- (void) fcntl(fd, F_SETFD, 1);
- (void) fcntl(fd, F_SETFL, FNDELAY);
- */
-
- *fdP = fd;
- return(STATUS_OK);
- }
-
- /*
- * StreamConnection -- create a new connection with client using
- * server port.
- *
- * This one should be non-blocking. The client could send us
- * part of a message. Would not do at all to have the server
- * block waiting for the message to complete. Not neccesary
- * to return the address, but it could conceivably be used
- * for protection checking.
- *
- * RETURNS: STATUS_OK or STATUS_ERROR
- */
- StreamConnection(server_fd,new_fdP,addrP)
- int server_fd;
- int *new_fdP;
- struct sockaddr_in *addrP;
- {
- long len = sizeof (struct sockaddr_in);
-
- if ((*new_fdP = accept(server_fd, (char *)addrP, &len)) < 0) {
- strcpy(PQerrormsg,"StreamConnection: accept failed\n");
- fprintf(stderr,PQerrormsg);
- return(STATUS_ERROR);
- }
- /* reset to non-blocking */
- fcntl(*new_fdP, F_SETFL, 1);
-
- return(STATUS_OK);
- }
-
- /*
- * StreamClose -- close a client/backend connection
- */
- StreamClose(sock)
- int sock;
- {
- close(sock);
- }
-
- /* ---------------------------
- * StreamOpen -- From client, initiate a connection with the
- * server (Postmaster).
- *
- * RETURNS: STATUS_OK or STATUS_ERROR
- *
- * NOTE: connection is NOT established just because this
- * routine exits. Local state is ok, but we haven't
- * spoken to the postmaster yet.
- *
- * ASSUME that the client doesn't need the net address of the
- * server after this routine exits.
- * ---------------------------
- */
- StreamOpen(hostName,portName,sockP)
- char *hostName;
- short portName;
- int *sockP;
- {
- struct sockaddr_in addr;
- struct hostent *hp;
- int sock;
-
- if (! hostName)
- hostName = "localhost";
-
- bzero((char *)&addr, sizeof addr);
-
- if ((hp = gethostbyname(hostName)) && hp->h_addrtype == AF_INET)
- bcopy(hp->h_addr, (char *)&(addr.sin_addr), hp->h_length);
- else {
- sprintf(PQerrormsg,
- "StreamPort: cannot find hostname '%s' for stream port\n",
- hostName);
- fprintf(stderr,PQerrormsg);
- return(STATUS_ERROR);
- }
-
- if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
- strcpy(PQerrormsg,"StreamPort: cannot make socket descriptor for port\n");
- fprintf(stderr,PQerrormsg);
- return(STATUS_ERROR);
- }
-
- addr.sin_family = AF_INET;
- addr.sin_port = htons(portName);
-
- *sockP = sock;
- if (! connect(sock, &addr, sizeof(addr)))
- return(STATUS_OK);
- else
- return(STATUS_ERROR);
- }
-